/* Created on 2006-10-14 */
package co.fitstart.entity.utils;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.PropertyUtils;

/**
 * BeanUtils simply encapsulate {@link PropertyUtils PropertyUtils} and
 * {@link org.apache.commons.beanutils.BeanUtils BeanUtils}
 * 
 * @author Downpour
 * @author lute
 * 
 * @see org.apache.commons.beanutils.PropertyUtils
 * @see org.apache.commons.beanutils.BeanUtils
 */
public abstract class BeanUtils {

    /**
     * Copies properties using {@link PropertyUtils#copyProperties(Object, Object) copyProperties}
     * 
     * @param dest    the destination object of the properties
     * @param source  the source object of the properties
     */
    public static void copyProperties(Object dest, Object source) {
        try {
            PropertyUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    /**
     * Populates bean from properties using
     * {@link org.apache.commons.beanutils.BeanUtils#populate(Object, Map) populate}
     * 
     * @param bean        JavaBean whose properties are being populated
     * @param properties  Map keyed by property name, with the corresponding 
     *                    (String or String[]) value(s) to be set
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    public static void populate(Object bean, Map properties) {
        try {
            org.apache.commons.beanutils.BeanUtils.populate(bean, properties);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * Describes bean using {@link org.apache.commons.beanutils.BeanUtils#describe(Object) describe}
     * 
     * @param bean  JavaBean whose properties are to be described
     * @return      Map keyed by property name, and property value as value
     */
    @SuppressWarnings({ "rawtypes" })
    public static Map describe(Object bean) {
        Map result = new HashMap();
        try {
            result = org.apache.commons.beanutils.BeanUtils.describe(bean);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * Describe bean to a Map without "class" key/value pair and underscore styled keys
     * 
     * @param bean  JavaBean whose properties are to be described
     * @return      Map keyed by property name with underscore styled, which has no
     *              property of its key is "class"
     */
    @SuppressWarnings("unchecked")
    public static Map<String, String> pureDescribeWithKeyUnderscored(Object bean) {
        Map<String, String> map = describe(bean);
        
        // Remove the "class" key/value pair
        if (map.containsKey("class")) {
            map.remove("class");
        }
        
        Map<String, String> result = new HashMap<String, String>();
        for (Iterator<Entry<String, String>> iterator = map.entrySet().iterator(); iterator.hasNext();) {
            
            Entry<String, String> entry = iterator.next();
            
            String key = StringUtils.convertCamelToUnderscore(entry.getKey());
            result.put(key, entry.getValue());
        }
        
        return result;
    }

    /**
     * Copies JavaBean properties from source bean to destination bean using
     * {@link org.apache.commons.beanutils.BeanUtils#copyProperties(Object, Object) copyProperties}
     * 
     * @param dest    JavaBean the properties are copied to
     * @param source  JavaBean the properties are copies from
     */
    public static void copyBean(Object dest, Object source) {
        try {
            org.apache.commons.beanutils.BeanUtils.copyProperties(dest, source);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    /**
     * Checks if the id property of nested JavaBean is not empty
     * 
     * @param bean      JavaBean contains nested JavaBean specified by property
     * @param property  name of the nested JavaBean
     * @return          {@code true} if the id property of nested JavaBean is not empty
     */
    public static boolean isPropertyNotEmpty(Object bean, String property) {
        return isPropertyNotEmpty(bean, property, "id");
    }

    /**
     * Checks if the specified property of nested JavaBean is not empty
     * 
     * @param bean                 JavaBean contains nested JavaBean specified by property 
     * @param property             name of the nested JavaBean
     * @param nestedCheckProperty  name of the property of the nested JavaBean
     * @return                     {@code true} if the specified property of nested JavaBean
     *                             is not empty
     */
    public static boolean isPropertyNotEmpty(Object bean, String property, String nestedCheckProperty) {
        if (bean == null)
            return false;
        
        try {
            Object target = PropertyUtils.getProperty(bean, property);
            if (target != null) {
                Object id = PropertyUtils.getProperty(target, nestedCheckProperty);
                if (id != null && id instanceof String && org.apache.commons.lang3.StringUtils.isNotBlank((String) id)) {
                    return true;
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        return false;
    }

}
